<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <meta name="keywords" content="imlgw,半岛铁盒,blog,Java博客,程序员,个人博客,java開發,程序員,個人博客,Java">
    <meta name="description" content="大悲无泪，大悟无言，大笑无声。">
    <meta name="author" content="Resolmi">
    
    <title>
        
            Socket网络编程 |
        
        Tadow
    </title>
    
<link rel="stylesheet" href="/css/style.css">

    <link rel="shortcut icon" href="https://static.imlgw.top/blog/20210731/BtJz541CcmJU.ico">
    <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/css/font-awesome.min.css">
    <script id="hexo-configurations">
    let KEEP = window.KEEP || {};
    KEEP.hexo_config = {"hostname":"imlgw.top","root":"/","language":"zh-CN","path":"search.json"};
    KEEP.theme_config = {"toc":{"enable":true,"number":true,"expand_all":true,"init_open":true},"style":{"primary_color":"#0066CC","avatar":"https://static.imlgw.top/blog/20210731/3C7hCSRR3lfq.png","favicon":"https://static.imlgw.top/blog/20210731/BtJz541CcmJU.ico","article_img_align":"left","left_side_width":"260px","content_max_width":"920px","hover":{"shadow":false,"scale":true},"first_screen":{"enable":true,"background_img":"/images/image.svg","description":"Keep It Simple & Stupid."},"scroll":{"progress_bar":{"enable":true},"percent":{"enable":true}}},"local_search":{"enable":true,"preload":false},"code_copy":{"enable":true,"style":"default"},"pjax":{"enable":true},"lazyload":{"enable":true},"version":"3.4.3"};
    KEEP.language_ago = {"second":"%s 秒前","minute":"%s 分钟前","hour":"%s 小时前","day":"%s 天前","week":"%s 周前","month":"%s 月前","year":"%s 年前"};
  </script>
<meta name="generator" content="Hexo 5.4.0"><link rel="stylesheet" href="/css/prism.css" type="text/css"></head>


<body>
<div class="progress-bar-container">
    
        <span class="scroll-progress-bar"></span>
    

    
        <span class="pjax-progress-bar"></span>
        <span class="pjax-progress-icon">
            <i class="fas fa-circle-notch fa-spin"></i>
        </span>
    
</div>


<main class="page-container">

    

    <div class="page-main-content">

        <div class="page-main-content-top">
            <header class="header-wrapper">

    <div class="header-content">
        <div class="left">
            
            <a class="logo-title" href="/">
                Tadow
            </a>
        </div>

        <div class="right">
            <div class="pc">
                <ul class="menu-list">
                    
                        <li class="menu-item">
                            <a class=""
                               href="/"
                            >
                                首页
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/archives"
                            >
                                归档
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/categories"
                            >
                                分类
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/sbe"
                            >
                                订阅
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/links"
                            >
                                友链
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/about"
                            >
                                关于
                            </a>
                        </li>
                    
                    
                        <li class="menu-item search search-popup-trigger">
                            <i class="fas fa-search"></i>
                        </li>
                    
                </ul>
            </div>
            <div class="mobile">
                
                    <div class="icon-item search search-popup-trigger"><i class="fas fa-search"></i></div>
                
                <div class="icon-item menu-bar">
                    <div class="menu-bar-middle"></div>
                </div>
            </div>
        </div>
    </div>

    <div class="header-drawer">
        <ul class="drawer-menu-list">
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/">首页</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/archives">归档</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/categories">分类</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/sbe">订阅</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/links">友链</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/about">关于</a>
                </li>
            
        </ul>
    </div>

    <div class="window-mask"></div>

</header>


        </div>

        <div class="page-main-content-middle">

            <div class="main-content">

                
                    <div class="fade-in-down-animation">
    <div class="article-content-container">

        <div class="article-title">
            <span class="title-hover-animation">Socket网络编程</span>
        </div>

        
            <div class="article-header">
                <div class="avatar">
                    <img src="https://static.imlgw.top/blog/20210731/3C7hCSRR3lfq.png">
                </div>
                <div class="info">
                    <div class="author">
                        <span class="name">Resolmi</span>
                        
                            <span class="author-label">BOSS</span>
                        
                    </div>
                    <div class="meta-info">
                        <div class="article-meta-info">
    <span class="article-date article-meta-item">
        <i class="fas fa-edit"></i>&nbsp;2019-07-19 00:00:00
    </span>
    
        <span class="article-categories article-meta-item">
            <i class="fas fa-folder"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/categories/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/">网络编程</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    
    
        <span class="article-tags article-meta-item">
            <i class="fas fa-tags"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/tags/Socket/">Socket</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    

    
    
        <span class="article-wordcount article-meta-item">
            <i class="fas fa-file-word"></i>&nbsp;<span>5.2k 字</span>
        </span>
    
    
        <span class="article-min2read article-meta-item">
            <i class="fas fa-clock"></i>&nbsp;<span>24 分钟</span>
        </span>
    
    
        <span class="article-pv article-meta-item">
            <i class="fas fa-eye"></i>&nbsp;<span id="busuanzi_value_page_pv"></span>
        </span>
    
</div>

                    </div>
                </div>
            </div>
        

        <div class="article-content markdown-body">
            <h3 id="Socket概述"><a href="#Socket概述" class="headerlink" title="Socket概述"></a>Socket概述</h3><p>​    套接字（socket）是一个抽象层，应用程序可以通过它发送或接收数据，可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中，并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。socket提供的函数是<strong>操作系统内核将“TCP/IP协议栈 + 底层网卡”抽象出来的一个个用户友好的函数，用于操纵本地的“TCP/IP协议栈 + 底层网卡”与远端的服务器/主机完成通信的任务。</strong></p>
<h3 id="TCP"><a href="#TCP" class="headerlink" title="TCP"></a>TCP</h3><p>🔸 TCP是面向连接的通信协议</p>
<p>🔸 通过<code>三次握手</code>建立连接，通讯完成时要拆除连接</p>
<p>🔸 由于TCP是面向连接的所以只能用于端到端的通讯</p>
<p>🔸 三次握手四次挥手</p>
<p>🔸 具有校验机制，可靠，数据传输稳定</p>
<h4 id="简单的Socket小案例"><a href="#简单的Socket小案例" class="headerlink" title="简单的Socket小案例"></a>简单的Socket小案例</h4><p><strong>Socket客户端</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> TcpDemo;</span><br><span class="line"><span class="keyword">import</span> java.io.*;</span><br><span class="line"><span class="keyword">import</span> java.net.*;</span><br><span class="line"><span class="keyword">import</span> java.util.Scanner;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> imlgw.top</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2019/7/7 9:45</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> REMOTE_PORT = <span class="number">20000</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> LOCAL_PORT = <span class="number">30000</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        Socket socket = creatSocket();</span><br><span class="line">        initSocket(socket);</span><br><span class="line">        <span class="comment">//setRecessAddress前</span></span><br><span class="line">        socket.bind(<span class="keyword">new</span> InetSocketAddress(InetAddress.getLocalHost(), LOCAL_PORT));</span><br><span class="line">        <span class="comment">//连接远程server</span></span><br><span class="line">        socket.connect(<span class="keyword">new</span> InetSocketAddress(InetAddress.getLocalHost(), REMOTE_PORT), <span class="number">3000</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;客户端已经发起连接&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;客户端信息:&quot;</span> + socket.getLocalAddress() + <span class="string">&quot;port:&quot;</span> + socket.getLocalPort());</span><br><span class="line">        System.out.println(<span class="string">&quot;服务端信息&quot;</span> + socket.getInetAddress() + <span class="string">&quot;port:&quot;</span> + socket.getPort());</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            sendMsg(socket);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            System.err.println(<span class="string">&quot;连接异常关闭！！！！&quot;</span>);</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            socket.close();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">initSocket</span><span class="params">(Socket socket)</span> <span class="keyword">throws</span> SocketException </span>&#123;</span><br><span class="line">        socket.setSoTimeout(<span class="number">3000</span>);</span><br><span class="line">        <span class="comment">//是否复用未完全关闭后的端口(TIME_WAIT状态)，必须在bind前，所以就不能通过构造器来绑定本地端口</span></span><br><span class="line">        socket.setReuseAddress(<span class="keyword">true</span>);</span><br><span class="line">        <span class="comment">//是否开启Nagle算法(默认开启) https://baike.baidu.com/item/Nagle%E7%AE%97%E6%B3%95</span></span><br><span class="line">        socket.setTcpNoDelay(<span class="keyword">false</span>);</span><br><span class="line">        <span class="comment">//长时间无数据相应的时候发送确认数据（心跳包）时间大约两个小时</span></span><br><span class="line">        socket.setKeepAlive(<span class="keyword">true</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">          close关闭后的处理</span></span><br><span class="line"><span class="comment">          这个Socket选项可以影响close方法的行为。</span></span><br><span class="line"><span class="comment">          false 0 默认情况 关闭后立即返回，底层系统接管输出流，将缓冲区的数据发送完成</span></span><br><span class="line"><span class="comment">          true 0 关闭后直接返回 缓冲区数据直接抛弃 直接发送RES结束命令到对方，无需经过2MSL等待</span></span><br><span class="line"><span class="comment">          true 200 关闭时最长阻塞200s 随后按第二情况处理</span></span><br><span class="line"><span class="comment">          (是s不是ms,开始搞错了 设置了20重启就会端口占用。。。。)</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">        socket.setSoLinger(<span class="keyword">true</span>, <span class="number">0</span>);</span><br><span class="line">       </span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">        设置紧急数据是否内敛,如果这个Socket选项打开，</span></span><br><span class="line"><span class="comment">        可以通过Socket类的sendUrgentData方法</span></span><br><span class="line"><span class="comment">        向服务器发送一个单字节的数据 这个单字节数据并不经过输出缓冲区，而是立即发出。</span></span><br><span class="line"><span class="comment">        虽然在客户端并不是使用OutputStream向服务器发送数据，</span></span><br><span class="line"><span class="comment">        但在服务端程序中这个单字节的数据是和其它的普通数据混在一起的</span></span><br><span class="line"><span class="comment">        因此，在服务端程序中并不知道由客户</span></span><br><span class="line"><span class="comment">        端发过来的数据是由OutputStream</span></span><br><span class="line"><span class="comment">        还是由sendUrgentData发过来的</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">        socket.setOOBInline(<span class="keyword">true</span>);</span><br><span class="line">        <span class="comment">//设置收发缓冲器大小，默认32K</span></span><br><span class="line">        socket.setReceiveBufferSize(<span class="number">64</span>*<span class="number">1024</span>);</span><br><span class="line">        socket.setSendBufferSize(<span class="number">64</span>*<span class="number">1024</span>);</span><br><span class="line">        <span class="comment">//设置性能参数的 优先级  短链接 延迟 带宽</span></span><br><span class="line">        socket.setPerformancePreferences(<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Socket <span class="title">creatSocket</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">        //无代理模式, 相当于空构造函数</span></span><br><span class="line"><span class="comment">        Socket socket = new Socket(Proxy.NO_PROXY);</span></span><br><span class="line"><span class="comment">        //HTTP代理模式传输的数据将通过www.imlgw.top转发</span></span><br><span class="line"><span class="comment">        socket = new Socket(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(Inet4Address.getByName(&quot;www.imlgw.top&quot;), 80)));</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        //下面两种方式回在创建的时候就去链接远程的服务器(具体看源码),然而一般情况下其实在连接之前我们还需要设置一些参数</span></span><br><span class="line"><span class="comment">        //新建一个套接字 链接到远程服务器和端口（本地端口为系统分配）</span></span><br><span class="line"><span class="comment">        socket = new Socket(&quot;imlgw.top&quot;, REMOTE_PORT);</span></span><br><span class="line"><span class="comment">        //新建套接字直接链接到远程端口 并绑定本地端口</span></span><br><span class="line"><span class="comment">        socket=new Socket(&quot;imlgw.top&quot;,REMOTE_PORT,InetAddress.getLocalHost(),LOCAL_PORT);</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//新建socket然后绑定到本地端口</span></span><br><span class="line">        Socket socket = <span class="keyword">new</span> Socket();</span><br><span class="line">        <span class="keyword">return</span> socket;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sendMsg</span><span class="params">(Socket socket)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="comment">//键盘的输入流</span></span><br><span class="line">        Scanner scanner = <span class="keyword">new</span> Scanner(System.in);</span><br><span class="line">        <span class="comment">//拿到socket的输出流</span></span><br><span class="line">        OutputStream socketOutputStream = socket.getOutputStream();</span><br><span class="line">        <span class="comment">//转换为打印流</span></span><br><span class="line">        PrintStream printStream = <span class="keyword">new</span> PrintStream(socketOutputStream);</span><br><span class="line">        <span class="comment">//socket的输入流</span></span><br><span class="line">        InputStream inputStream = socket.getInputStream();</span><br><span class="line">        <span class="comment">//转换位buffer流</span></span><br><span class="line">        BufferedReader bufferedReader = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(inputStream));</span><br><span class="line"></span><br><span class="line">        <span class="keyword">boolean</span> flag = <span class="keyword">true</span>;</span><br><span class="line">        <span class="keyword">do</span> &#123;</span><br><span class="line">            <span class="comment">//客户端发送消息</span></span><br><span class="line">            printStream.println(scanner.nextLine());</span><br><span class="line">            <span class="comment">//服务端的响应</span></span><br><span class="line">            String s = bufferedReader.readLine();</span><br><span class="line">            <span class="keyword">if</span> (<span class="string">&quot;bye&quot;</span>.equals(s)) &#123;</span><br><span class="line">                flag = <span class="keyword">false</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;服务端响应：&quot;</span> + s);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">while</span> (flag);</span><br><span class="line">        bufferedReader.close();</span><br><span class="line">        printStream.close();</span><br><span class="line">        scanner.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>Socket服务端</strong></p>
<p> 这里为了同时处理多个客户端设计成了<strong>多线程</strong>异步的模式</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> TcpDemo;</span><br><span class="line"><span class="keyword">import</span> java.io.BufferedReader;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStreamReader;</span><br><span class="line"><span class="keyword">import</span> java.io.PrintStream;</span><br><span class="line"><span class="keyword">import</span> java.net.*;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> imlgw.top</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2019/7/7 9:46</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Server</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SERVER_PORT = <span class="number">20000</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        ServerSocket server = creatServerSocket();</span><br><span class="line">        initServerSocket(server);</span><br><span class="line">        <span class="comment">//初始化之后再绑定，不然一些设置会失效，比如 setReuseAddress</span></span><br><span class="line">        server.bind(<span class="keyword">new</span> InetSocketAddress(InetAddress.getLocalHost(), SERVER_PORT), <span class="number">50</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;服务器准备就绪&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;服务端信息&quot;</span> + server.getInetAddress() + <span class="string">&quot; port:&quot;</span> + server.getLocalPort());</span><br><span class="line">        <span class="comment">//监听客户端的消息</span></span><br><span class="line">        <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">            <span class="comment">//阻塞方法</span></span><br><span class="line">            Socket client = server.accept();</span><br><span class="line">            ClientHandle clientHandle = <span class="keyword">new</span> ClientHandle(client);</span><br><span class="line">            <span class="keyword">new</span> Thread(clientHandle).start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">initServerSocket</span><span class="params">(ServerSocket server)</span> <span class="keyword">throws</span> SocketException </span>&#123;</span><br><span class="line">        <span class="comment">//同client</span></span><br><span class="line">        server.setReuseAddress(<span class="keyword">true</span>);</span><br><span class="line">        <span class="comment">//设置accept的buffer</span></span><br><span class="line">        server.setReceiveBufferSize(<span class="number">64</span> * <span class="number">1024</span>);</span><br><span class="line">        <span class="comment">//设置timeout</span></span><br><span class="line">        <span class="comment">//server.setSoTimeout(2000);</span></span><br><span class="line">        <span class="comment">//设置性能参数,连接前设置</span></span><br><span class="line">        server.setPerformancePreferences(<span class="number">1</span>, <span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> ServerSocket <span class="title">creatServerSocket</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        ServerSocket server = <span class="keyword">new</span> ServerSocket();</span><br><span class="line">        <span class="comment">//绑定端口 backlog:新连接队列的长度限制,不是链接的数量,是允许等待的队列长度</span></span><br><span class="line">        <span class="comment">//server.bind(new InetSocketAddress(InetAddress.getLocalHost(),SERVER_PORT),50);</span></span><br><span class="line">        <span class="comment">//server =new ServerSocket(SERVER_PORT,50); 等效方案</span></span><br><span class="line">        <span class="comment">//server =new ServerSocket(SERVER_PORT,50,InetAddress.getLocalHost());</span></span><br><span class="line">        <span class="keyword">return</span> server;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ClientHandle</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> Socket socket;</span><br><span class="line"></span><br><span class="line">        ClientHandle(Socket client) &#123;</span><br><span class="line">            <span class="keyword">this</span>.socket = client;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//接收消息</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;新客户端连接：&quot;</span> + socket.getInetAddress() + <span class="string">&quot;port：&quot;</span> + socket.getPort());</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//输入流获取信息</span></span><br><span class="line">                BufferedReader reader = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(socket.getInputStream()));</span><br><span class="line">                <span class="comment">//输出流响应客户端</span></span><br><span class="line">                PrintStream printStream = <span class="keyword">new</span> PrintStream(socket.getOutputStream());</span><br><span class="line">                <span class="keyword">boolean</span> flag = <span class="keyword">true</span>;</span><br><span class="line">                <span class="keyword">do</span> &#123;</span><br><span class="line">                    String s = reader.readLine();</span><br><span class="line">                    <span class="keyword">if</span> (<span class="string">&quot;bye&quot;</span>.equalsIgnoreCase(s)) &#123;</span><br><span class="line">                        flag = <span class="keyword">false</span>;</span><br><span class="line">                        System.out.println(<span class="string">&quot;客户端关闭了连接&quot;</span>);</span><br><span class="line">                        printStream.println(<span class="string">&quot;bye&quot;</span>);</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        System.out.println(s);</span><br><span class="line">                        printStream.println(<span class="string">&quot;字符串长度#&quot;</span> + s.length());</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">while</span> (flag);</span><br><span class="line">                reader.close();</span><br><span class="line">                printStream.close();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    socket.close();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>具体的一些常用的方法解释都在代码的注释中 <a class="link"   target="_blank" rel="noopener" href="https://elf8848.iteye.com/blog/1739598" >参考<i class="fas fa-external-link-alt"></i></a></p>
</blockquote>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://static.imlgw.top/image/20190707/eBKfzShaiIHU.png?imageslim"
                      alt="mark"
                ></p>
<h4 id="传输基本数据类型"><a href="#传输基本数据类型" class="headerlink" title="传输基本数据类型"></a>传输基本数据类型</h4><p>上面的哪个小案例传送的都是字符串类型的数据，也许有同学会说这些基本类型不都是可以通过字符串来传吗？为什么要费那个劲去传这些基本类型？其实不然，这里假设要传送的是 int类型的12345678 ，如果通过 <code>int</code> 来传输只要在范围内都是<strong>4</strong>个字节大小固定，然而通过<code>String</code>传送将会是”12345678” 也就是<strong>8</strong>个字节，消耗要比使用<code>int</code>要大，而且长度不固定，不方便后期接受的长度判断。</p>
<p> <strong><code>int</code> 类型</strong>  </p>
<p>在网络上传输的都是以<strong>Byte</strong>为基本单位，如果要传送<strong>int</strong>我们就需要将<strong>int</strong>转换为<strong>byte</strong>，一个<strong>int</strong>是4个字节，我们可以将其转换为一个**byte[]**数组，废话不多说，上代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> imlgw.top</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2019/7/11 13:23</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ByteTools</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">byte</span>[] int2byte(<span class="keyword">int</span> a) &#123;</span><br><span class="line">        <span class="comment">//无符号右移</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">byte</span>[]&#123;</span><br><span class="line">                (<span class="keyword">byte</span>) (a &gt;&gt;&gt; <span class="number">24</span>),</span><br><span class="line">                (<span class="keyword">byte</span>) (a &gt;&gt;&gt; <span class="number">16</span>),</span><br><span class="line">                (<span class="keyword">byte</span>) (a &gt;&gt;&gt; <span class="number">8</span>),</span><br><span class="line">                (<span class="keyword">byte</span>) (a)</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">byte2int</span><span class="params">(<span class="keyword">byte</span>[] a)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//&amp;0xff--&gt;转换为int 将高位补0,低8位不变</span></span><br><span class="line">        <span class="comment">//-127 ：10000001(补) &amp;0xff --&gt; 00000000 00000000 00000000 10000001</span></span><br><span class="line">        <span class="keyword">return</span> a[<span class="number">3</span>] &amp; <span class="number">0xff</span> | (a[<span class="number">2</span>] &amp; <span class="number">0xff</span>) &lt;&lt; <span class="number">8</span> | (a[<span class="number">1</span>] &amp; <span class="number">0xff</span>) &lt;&lt; <span class="number">16</span> | (a[<span class="number">0</span>] &amp; <span class="number">0xff</span>) &lt;&lt; <span class="number">24</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>为什么这样做的一些细节可以参考 <a class="link"   target="_blank" rel="noopener" href="https://www.cnblogs.com/think-in-java/p/5527389.html" >这篇博客<i class="fas fa-external-link-alt"></i></a></p>
<p>有了这个工具类我们就可以将<strong>int</strong>转换为**byte[]**后进行传输，同时接收端也可以通过这个方法将数据还原。</p>
<p>等等🙄 ，这样一来不是所有的类型对应的都要去写个这样的转换的方法？那还是有点麻烦的，而且也没有什么技术含量，所以这样的事情<strong>JDK</strong>帮我们做了</p>
<p><strong><code>ByteBuffer</code>**：nio中的一个包，这里我还不太熟悉这个具体的作用，目前只知道可以用来包装</strong>byte[]**，然后可以实现上面的类型转换</p>
<p><strong>Client发送端</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">byte</span>[] buffer=<span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">256</span>];</span><br><span class="line"><span class="comment">//包装buffer (装饰器模式？</span></span><br><span class="line">ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);</span><br><span class="line"><span class="comment">//byte  1</span></span><br><span class="line">byteBuffer.put((<span class="keyword">byte</span>) <span class="number">126</span>);</span><br><span class="line"><span class="comment">//int 类型 4</span></span><br><span class="line">byteBuffer.putInt(<span class="number">123</span>);</span><br><span class="line"><span class="comment">//char 2(unicode)</span></span><br><span class="line">byteBuffer.putChar(<span class="string">&#x27;A&#x27;</span>);</span><br><span class="line"><span class="comment">//long 8</span></span><br><span class="line">byteBuffer.putLong(<span class="number">323333231234124321L</span>);</span><br><span class="line"><span class="keyword">boolean</span> isOk=<span class="keyword">true</span>;</span><br><span class="line"><span class="comment">//byte 1</span></span><br><span class="line">byteBuffer.put((<span class="keyword">byte</span>) (isOk?<span class="number">1</span>:<span class="number">0</span>));</span><br><span class="line"><span class="comment">//float 4</span></span><br><span class="line">byteBuffer.putFloat(<span class="number">123.2132F</span>);</span><br><span class="line"><span class="comment">//double 8 =28</span></span><br><span class="line">byteBuffer.putDouble(<span class="number">231.1412421321</span>);</span><br><span class="line"><span class="comment">//String 10</span></span><br><span class="line">byteBuffer.put(<span class="string">&quot;HelloWorld&quot;</span>.getBytes());</span><br><span class="line"><span class="comment">//发送 38 Byte </span></span><br><span class="line">socketOutputStream.write(buffer,<span class="number">0</span>,byteBuffer.position());</span><br></pre></td></tr></table></figure>

<p>需要注意的地方就是最后<strong>write</strong>的时候，第二个参数<strong>len</strong>，直接传<strong>position()</strong>,就可以了，不用+1，这个position是下一个字节位置</p>
<blockquote>
<p>这里其实我看的教程这里是加1了的，最后接收过来的数据长度死活对不上，我开始还以为是什么<strong>内存对齐</strong>，什么乱七八糟的然后才发现是这里有问题。。。</p>
</blockquote>
<p><strong>Server接收端</strong></p>
<p>基本类型的读取与上面对应的<strong>get</strong>，最后一个String需要注意，直接用原始的<strong>buffer</strong>就可以了，不需要借助<strong>ByteBuffer</strong>，这里同样后面不用-1</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">String str = <span class="keyword">new</span> String(buffer, byteBuffer.position(), readByteCount-byteBuffer.position());</span><br></pre></td></tr></table></figure>

<p><strong>测试结果</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">服务器准备就绪</span><br><span class="line">服务端信息LAPTOP-V5R5ABUJ/<span class="number">192.168</span><span class="number">.25</span><span class="number">.1</span> port:<span class="number">20000</span></span><br><span class="line">新客户端连接：/<span class="number">192.168</span><span class="number">.25</span>.1port：<span class="number">30000</span></span><br><span class="line">当前下标<span class="number">28</span></span><br><span class="line">接受到Client数据长度(<span class="keyword">byte</span>)：<span class="number">38</span></span><br><span class="line">Client发送的数据:</span><br><span class="line"><span class="number">126</span></span><br><span class="line"><span class="number">123</span></span><br><span class="line">A</span><br><span class="line"><span class="number">323333231234124321</span></span><br><span class="line"><span class="keyword">true</span></span><br><span class="line"><span class="number">123.2132</span></span><br><span class="line"><span class="number">231.1412421321</span></span><br><span class="line">HelloWorld</span><br></pre></td></tr></table></figure>

<h3 id="UDP"><a href="#UDP" class="headerlink" title="UDP"></a>UDP</h3><p>🔸  UDP是无连接的，即发送数据之前不需要建立连接，因此减少了开销和发送数据之前的时延。</p>
<p>🔸  UDP使用尽最大努力交付，即不保证可靠交付，因此主机不需要维持复杂的连接状态表。</p>
<p>🔸   UDP是面向报文的。发送方的UDP对应用程序交下来的报文，在添加首部后就向下交付IP层。UDP对应用层交下来的报文，既不合并，也不拆分，而是保留这些报文的边界。因此，应用程序必须选择合适大小的报文。</p>
<p>🔸   UDP没有拥塞控制，因此网络出现的拥塞不会使源主机的发送速率降低。很多的实时应用（如IP电话、实时视频会议等）要去源主机以恒定的速率发送数据，并且允许在网络发生拥塞时丢失一些数据，但却不允许数据有太多的时延。UDP正好符合这种要求。</p>
<p>🔸   UDP支持一对一、一对多、多对一和多对多的交互通信。</p>
<p>🔸   UDP的首部开销小，只有8个字节，比TCP的20个字节的首部要短。</p>
<h4 id="单播"><a href="#单播" class="headerlink" title="单播"></a>单播</h4><p><strong>消息接收者</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.net.DatagramPacket;</span><br><span class="line"><span class="keyword">import</span> java.net.DatagramSocket;</span><br><span class="line"><span class="keyword">import</span> java.net.SocketAddress;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> imlgw.top</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2019/7/7 21:31</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UDPProvide</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="comment">//监听20000端口</span></span><br><span class="line">        DatagramSocket socket=<span class="keyword">new</span> DatagramSocket(<span class="number">20000</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;UDPProvide is start....&quot;</span>);</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">byte</span>[] buf=<span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">512</span>];</span><br><span class="line">        <span class="comment">//构建接受的DatagramPacket</span></span><br><span class="line">        DatagramPacket udp_receive=<span class="keyword">new</span> DatagramPacket(buf,buf.length);</span><br><span class="line">        <span class="comment">//构建接受的DatagramPacket (阻塞)</span></span><br><span class="line">        socket.receive(udp_receive);</span><br><span class="line">        <span class="comment">//获取发送人的SocketAddress</span></span><br><span class="line">        SocketAddress socketAddress = udp_receive.getSocketAddress();</span><br><span class="line">        <span class="keyword">int</span> datalen = udp_receive.getLength();</span><br><span class="line">        <span class="comment">//获取发送的数据</span></span><br><span class="line">        String receive=<span class="keyword">new</span> String(udp_receive.getData(),<span class="number">0</span>,datalen);</span><br><span class="line">        System.out.println(<span class="string">&quot;receive from the: &quot;</span>+socketAddress);</span><br><span class="line">        System.out.println(<span class="string">&quot;receive data: &quot;</span>+ receive);</span><br><span class="line">        <span class="comment">//构建响应的DatagramPacket</span></span><br><span class="line">        <span class="keyword">byte</span>[] bytes = (<span class="string">&quot;provider receive the data success &quot;</span>+datalen).getBytes();</span><br><span class="line">        DatagramPacket udp_sendBack=<span class="keyword">new</span> DatagramPacket(bytes,bytes.length,socketAddress);</span><br><span class="line">        socket.send(udp_sendBack);</span><br><span class="line">        <span class="comment">//结束</span></span><br><span class="line">        System.out.println(<span class="string">&quot;UDPProvide Finished.&quot;</span>);</span><br><span class="line">        socket.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>消息发送者</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.net.DatagramPacket;</span><br><span class="line"><span class="keyword">import</span> java.net.DatagramSocket;</span><br><span class="line"><span class="keyword">import</span> java.net.InetAddress;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> imlgw.top</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2019/7/7 21:31</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UDPSearch</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;UDPSearch is ready...&quot;</span>);</span><br><span class="line">        <span class="comment">//构建socket</span></span><br><span class="line">        DatagramSocket socket = <span class="keyword">new</span> DatagramSocket();</span><br><span class="line">        <span class="keyword">byte</span>[] buff= <span class="string">&quot;hello world&quot;</span>.getBytes();</span><br><span class="line">        <span class="comment">//构建发送段</span></span><br><span class="line">        DatagramPacket udp_send=<span class="keyword">new</span> DatagramPacket(buff,buff.length);</span><br><span class="line">        <span class="comment">//指定对方ip</span></span><br><span class="line">        udp_send.setAddress(InetAddress.getLocalHost());</span><br><span class="line">        udp_send.setPort(<span class="number">20000</span>);</span><br><span class="line">        socket.send(udp_send);</span><br><span class="line">        <span class="comment">//获取响应段</span></span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">byte</span>[] buf=<span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">512</span>];</span><br><span class="line">        DatagramPacket udp_receive=<span class="keyword">new</span> DatagramPacket(buf,buf.length);</span><br><span class="line">        socket.receive(udp_receive);</span><br><span class="line">        String s = <span class="keyword">new</span> String(udp_receive.getData(), <span class="number">0</span>, udp_receive.getLength());</span><br><span class="line">        System.out.println(s);</span><br><span class="line">        System.out.println(<span class="string">&quot;UDPSearch is over&quot;</span>);</span><br><span class="line">        socket.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="多播-amp-广播"><a href="#多播-amp-广播" class="headerlink" title="多播&amp;广播"></a>多播&amp;广播</h4><p><strong>消息建造器</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> imlgw.top</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2019/7/8 8:48</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MessageCreator</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String SN_HEADER = <span class="string">&quot;收到暗号,我是SN:&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String PORT_HEADER = <span class="string">&quot;这是暗号,请回送到该端口:&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">buildWithPort</span><span class="params">(<span class="keyword">int</span> port)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> PORT_HEADER + port;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">parsePort</span><span class="params">(String sn)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (sn.startsWith(PORT_HEADER)) &#123;</span><br><span class="line">            <span class="keyword">return</span> Integer.parseInt(sn.substring(PORT_HEADER.length()));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span>  -<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">buildWithSn</span><span class="params">(String sn)</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> SN_HEADER+sn;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">parseSn</span><span class="params">(String sn)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(sn.startsWith(SN_HEADER))&#123;</span><br><span class="line">            <span class="keyword">return</span> sn.substring(SN_HEADER.length());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>消息接受者</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> UdpDemo2;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.net.*;</span><br><span class="line"><span class="keyword">import</span> java.util.UUID;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> imlgw.top</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2019/7/7 21:31</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UDPProvide</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> PROVIDE_LISTEN_PORT = <span class="number">20000</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        String sn = UUID.randomUUID().toString();</span><br><span class="line">        Provider provider = <span class="keyword">new</span> Provider(sn);</span><br><span class="line">        <span class="keyword">new</span> Thread(provider).start();</span><br><span class="line">        System.in.read();</span><br><span class="line">        provider.shutdown();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Provider</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">volatile</span> <span class="keyword">boolean</span> isDone = <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">public</span> DatagramSocket socket = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">final</span> String sn;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Provider</span><span class="params">(String sn)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.sn = sn;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;UDPProvide is start....&quot;</span>);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                socket = <span class="keyword">new</span> DatagramSocket(PROVIDE_LISTEN_PORT);</span><br><span class="line">                <span class="keyword">while</span> (!isDone) &#123;</span><br><span class="line">                    <span class="keyword">final</span> <span class="keyword">byte</span>[] buf = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">512</span>];</span><br><span class="line">                    <span class="comment">//构建接受的DatagramPacket</span></span><br><span class="line">                    DatagramPacket udp_receive = <span class="keyword">new</span> DatagramPacket(buf, buf.length);</span><br><span class="line">                    <span class="comment">//接受DatagramPacket (阻塞)</span></span><br><span class="line">                    socket.receive(udp_receive);</span><br><span class="line">                    <span class="comment">//获取发送人的SocketAddress</span></span><br><span class="line">                    InetSocketAddress socketAddress = (InetSocketAddress) udp_receive.getSocketAddress();</span><br><span class="line">                    <span class="keyword">int</span> datalen = udp_receive.getLength();</span><br><span class="line">                    <span class="comment">//获取发送过来的数据</span></span><br><span class="line">                    String receive = <span class="keyword">new</span> String(udp_receive.getData(), <span class="number">0</span>, datalen);</span><br><span class="line">                    <span class="comment">//打印获取到的数据</span></span><br><span class="line">                    System.out.println(<span class="string">&quot;receive from the: &quot;</span> + socketAddress);</span><br><span class="line">                    System.out.println(<span class="string">&quot;receive data: &quot;</span> + receive);</span><br><span class="line">                    <span class="comment">//解析sn,获取需要回送的端口</span></span><br><span class="line">                    <span class="keyword">int</span> port = MessageCreator.parsePort(receive);</span><br><span class="line">                    <span class="keyword">if</span> (port != -<span class="number">1</span>) &#123;</span><br><span class="line">                        <span class="comment">//构建回送的DatagramPacket</span></span><br><span class="line">                        <span class="keyword">byte</span>[] responseBody = MessageCreator.buildWithSn(sn).getBytes();</span><br><span class="line">                        DatagramPacket udp_sendBack = <span class="keyword">new</span> DatagramPacket(responseBody, <span class="number">0</span>, responseBody.length, socketAddress.getAddress(), port);</span><br><span class="line">                        socket.send(udp_sendBack);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                <span class="comment">// e.printStackTrace();</span></span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                closeRes();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//结束</span></span><br><span class="line">            System.out.println(<span class="string">&quot;UDPProvide Finished.&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">shutdown</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            isDone = <span class="keyword">true</span>;</span><br><span class="line">            <span class="comment">//这里仅仅isDone=true 远远不够,因为socket.receive是一个永久阻塞的方法</span></span><br><span class="line">            <span class="comment">//所以下面还要close这个socket这样就会捕获到一个异常然后结束</span></span><br><span class="line">            closeRes();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">closeRes</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (socket != <span class="keyword">null</span>) &#123;</span><br><span class="line">                socket.close();</span><br><span class="line">                socket = <span class="keyword">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>与上面不同的是这里为了随时可以停止将其构建成了异步线程，当接受到终止信号的时候就会改变状态量，并close资源，然后利用异常停止线程。</p>
<p><strong>消息发送者</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> UdpDemo2;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.net.*;</span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.CountDownLatch;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> imlgw.top</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2019/7/7 21:31</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UDPSearch</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SEARCH_LISTEN_PORT = <span class="number">30000</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> DatagramSocket socket = <span class="keyword">null</span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, InterruptedException </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;UDPSearch is start...&quot;</span>);</span><br><span class="line">        Listener listen = listen();</span><br><span class="line">        sendBoard();</span><br><span class="line">        System.in.read();</span><br><span class="line">        List&lt;Device&gt; devices = listen.closeAndGetDeviceList();</span><br><span class="line">        <span class="keyword">for</span> (Device device : devices) &#123;</span><br><span class="line">            System.out.println(device);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Listener <span class="title">listen</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;UDPSearch Listener is start&quot;</span>);</span><br><span class="line">        CountDownLatch countDownLatch=<span class="keyword">new</span> CountDownLatch(<span class="number">1</span>);</span><br><span class="line">        Listener listener=<span class="keyword">new</span> Listener(SEARCH_LISTEN_PORT,countDownLatch);</span><br><span class="line">        <span class="keyword">new</span> Thread(listener).start();</span><br><span class="line">        countDownLatch.await();</span><br><span class="line">        <span class="keyword">return</span> listener;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sendBoard</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="comment">//系统自动分配的端口</span></span><br><span class="line">        DatagramSocket socket = <span class="keyword">new</span> DatagramSocket();</span><br><span class="line">        <span class="comment">//构建socket</span></span><br><span class="line">        <span class="keyword">byte</span>[] buff = MessageCreator.buildWithPort(SEARCH_LISTEN_PORT).getBytes();</span><br><span class="line">        <span class="comment">//构建发送段</span></span><br><span class="line">        DatagramPacket udp_send = <span class="keyword">new</span> DatagramPacket(buff, buff.length);</span><br><span class="line">        <span class="comment">//广播地址</span></span><br><span class="line">        udp_send.setAddress(InetAddress.getByName(<span class="string">&quot;255.255.255.255&quot;</span>));</span><br><span class="line">        <span class="comment">//接收方的端口</span></span><br><span class="line">        udp_send.setPort(UDPProvide.PROVIDE_LISTEN_PORT);</span><br><span class="line">        socket.send(udp_send);</span><br><span class="line">        socket.close();</span><br><span class="line">        System.out.println(<span class="string">&quot;UDPSearch Board is over&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Device</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> port;</span><br><span class="line">        String ip;</span><br><span class="line">        String sn;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Device</span><span class="params">(<span class="keyword">int</span> port, String ip, String sn)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.port = port;</span><br><span class="line">            <span class="keyword">this</span>.ip = ip;</span><br><span class="line">            <span class="keyword">this</span>.sn = sn;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;Device&#123;&quot;</span> +</span><br><span class="line">                    <span class="string">&quot;port=&quot;</span> + port +</span><br><span class="line">                    <span class="string">&quot;, ip=&#x27;&quot;</span> + ip + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                    <span class="string">&quot;, sn=&#x27;&quot;</span> + sn + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                    <span class="string">&#x27;&#125;&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Listener</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">int</span> listenPort;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> CountDownLatch countDownLatch;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> List&lt;Device&gt; deviceList = <span class="keyword">new</span> ArrayList&lt;Device&gt;();</span><br><span class="line">        <span class="comment">//private static DatagramSocket socket = null;</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">boolean</span> isDone = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Listener</span><span class="params">(<span class="keyword">int</span> listenPort, CountDownLatch countDownLatch)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.listenPort = listenPort;</span><br><span class="line">            <span class="keyword">this</span>.countDownLatch = countDownLatch;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="comment">//通知已经启动</span></span><br><span class="line">            countDownLatch.countDown();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                socket = <span class="keyword">new</span> DatagramSocket(listenPort);</span><br><span class="line">                <span class="keyword">while</span> (!isDone) &#123;</span><br><span class="line">                    <span class="keyword">final</span> <span class="keyword">byte</span>[] buf = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">512</span>];</span><br><span class="line">                    <span class="comment">//构建接受的DatagramPacket</span></span><br><span class="line">                    DatagramPacket udp_receive = <span class="keyword">new</span> DatagramPacket(buf, buf.length);</span><br><span class="line">                    <span class="comment">//接受DatagramPacket (阻塞)</span></span><br><span class="line">                    socket.receive(udp_receive);</span><br><span class="line">                    <span class="comment">//获取发送人的SocketAddress</span></span><br><span class="line">                    InetSocketAddress socketAddress = (InetSocketAddress) udp_receive.getSocketAddress();</span><br><span class="line">                    <span class="keyword">int</span> datalen = udp_receive.getLength();</span><br><span class="line">                    <span class="comment">//获取发送过来的数据</span></span><br><span class="line">                    String sn = <span class="keyword">new</span> String(udp_receive.getData(), <span class="number">0</span>, datalen);</span><br><span class="line">                    System.out.println(<span class="string">&quot;back from the：&quot;</span> + socketAddress);</span><br><span class="line">                    System.out.println(<span class="string">&quot;back data：&quot;</span> + sn);</span><br><span class="line">                    sn=MessageCreator.parseSn(sn);</span><br><span class="line">                    <span class="keyword">if</span> (sn != <span class="keyword">null</span>) &#123;</span><br><span class="line">                        deviceList.add(<span class="keyword">new</span> Device(socketAddress.getPort(), socketAddress.getAddress().toString(), sn));</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">               <span class="comment">//e.printStackTrace();</span></span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                closeRes();</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;UDPSearch Listener is Finished...&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">closeRes</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (socket != <span class="keyword">null</span>) &#123;</span><br><span class="line">                socket.close();</span><br><span class="line">                socket = <span class="keyword">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function">List&lt;Device&gt; <span class="title">closeAndGetDeviceList</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            isDone = <span class="keyword">true</span>;</span><br><span class="line">            closeRes();</span><br><span class="line">            <span class="keyword">return</span> deviceList;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>这里需要注意的就是广播的地址<strong>255.255.255.255</strong></p>
<blockquote>
<p>如果是在局域网内和其他机器通信需要关闭虚拟机的网卡，不然是走的虚拟机的网卡，其他机器接收不到。(我说怎么发送的<strong>IP</strong>不是我的本机的<strong>IP</strong>)</p>
</blockquote>
<p>🔸UDP是面向无连接的通讯协议，基于用户数据报的协议</p>
<p>🔸UDP数据包括目的端口号和源端口号信息</p>
<p>🔸通讯不需要连接，所以可以实现广播发送，并不局限于端到端</p>
<p>🔸结构简单，无校验，速度快，容易丢包，可广播</p>
<p>🔸他一旦把应用程序发给网络层的数据发送出去就不保留数据备份</p>
<h3 id="UDP辅助TCP-实现点对点传输"><a href="#UDP辅助TCP-实现点对点传输" class="headerlink" title="UDP辅助TCP 实现点对点传输"></a>UDP辅助TCP 实现点对点传输</h3><p>客户端先利用<code>UDP</code>向局域网发送广播，然后对应的服务器接收到之后就会将对应的<code>TCP</code>的端口回送给客户端，然后二者进行TCP的双向通信，代码太多这里就只放一下<code>Server</code>端的<code>Handler</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> udp_tcp_concurrency.server.handle;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> udp_tcp_concurrency.utils.CloseUtils;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.*;</span><br><span class="line"><span class="keyword">import</span> java.net.Socket;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.ExecutorService;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.Executors;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 用来处理客户端的类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> imlgw.top</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2019/7/17 11:48</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span>  <span class="class"><span class="keyword">class</span> <span class="title">ClientHandler</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Socket socket;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ServerReadHandler serverReadHandler;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ServerWriterHandler serverWriterHandle;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> CloseNotify closeNotify;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ClientHandler</span><span class="params">(Socket socket, CloseNotify closeNotify)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.socket = socket;</span><br><span class="line">        <span class="keyword">this</span>.serverReadHandler = <span class="keyword">new</span> ServerReadHandler(socket.getInputStream());</span><br><span class="line">        <span class="keyword">this</span>.serverWriterHandle = <span class="keyword">new</span> ServerWriterHandler(socket.getOutputStream());</span><br><span class="line">        <span class="keyword">this</span>.closeNotify = closeNotify;</span><br><span class="line">        System.out.println(<span class="string">&quot;新客户端连接：&quot;</span> + socket.getInetAddress() + <span class="string">&quot;port：&quot;</span> + socket.getPort());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">send</span><span class="params">(String str)</span> </span>&#123;</span><br><span class="line">        serverWriterHandle.send(str);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//从外界关闭</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">stop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        serverReadHandler.stopRead();</span><br><span class="line">        serverWriterHandle.stopWriter();</span><br><span class="line">        CloseUtils.close(socket);</span><br><span class="line">        System.out.println(<span class="string">&quot;客户端已经退出&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;address:&quot;</span> + socket.getInetAddress() + <span class="string">&quot;,port:&quot;</span> + socket.getPort());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//自我关闭---&gt;自闭</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">stopByMyself</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        stop();</span><br><span class="line">        closeNotify.onSelfClosed(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//读取并打印到屏幕(启动ClientReadHandle线程)</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">read2Print</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">new</span> Thread(serverReadHandler).start();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  将已经关闭的handle暴露给TCPServer然后从list中移除</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">CloseNotify</span></span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">onSelfClosed</span><span class="params">(ClientHandler clientHandler)</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 处理服务端用于读取客户端消息的 Handle</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">ServerReadHandler</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">boolean</span> done = <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> InputStream inputStream;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">ServerReadHandler</span><span class="params">(InputStream inputStream)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.inputStream = inputStream;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//输入流获取信息</span></span><br><span class="line">                BufferedReader reader = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(inputStream));</span><br><span class="line">                <span class="keyword">do</span> &#123;</span><br><span class="line">                    String s = reader.readLine();</span><br><span class="line">                    <span class="keyword">if</span> (s == <span class="keyword">null</span>) &#123;</span><br><span class="line">                        System.out.println(<span class="string">&quot;客户端已经无法发送数据&quot;</span>);</span><br><span class="line">                        <span class="comment">//结束当前Handle</span></span><br><span class="line">                        ClientHandler.<span class="keyword">this</span>.stopByMyself();</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="comment">//打印到屏幕</span></span><br><span class="line">                    System.out.println(s);</span><br><span class="line">                &#125; <span class="keyword">while</span> (!done);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!done) &#123;</span><br><span class="line">                    <span class="comment">//非正常关闭</span></span><br><span class="line">                    System.err.println(<span class="string">&quot;连接异常断开&quot;</span>+e.getMessage());</span><br><span class="line">                    ClientHandler.<span class="keyword">this</span>.stopByMyself();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                CloseUtils.close(inputStream);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">stopRead</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            done = <span class="keyword">true</span>;</span><br><span class="line">            CloseUtils.close(inputStream);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 处理服务端向客户端发送消息的Handle</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">ServerWriterHandler</span>  </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">boolean</span> done = <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> PrintStream printStream;</span><br><span class="line">        <span class="comment">//线程池</span></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> ExecutorService executorService;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">ServerWriterHandler</span><span class="params">(OutputStream outputStream)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.printStream = <span class="keyword">new</span> PrintStream(outputStream);</span><br><span class="line">            <span class="comment">//单例线程池</span></span><br><span class="line">            executorService = Executors.newSingleThreadExecutor();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">send</span><span class="params">(String str)</span> </span>&#123;</span><br><span class="line">            <span class="comment">//这里如果不用线程池</span></span><br><span class="line">            executorService.submit(<span class="keyword">new</span> WriteRunnable(str));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//线程池的Runnable</span></span><br><span class="line">        <span class="class"><span class="keyword">class</span> <span class="title">WriteRunnable</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line">            <span class="keyword">private</span> <span class="keyword">final</span> String msg;</span><br><span class="line"></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="title">WriteRunnable</span><span class="params">(String msg)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">this</span>.msg = msg;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="keyword">if</span>(ServerWriterHandler.<span class="keyword">this</span>.done)&#123;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    ServerWriterHandler.<span class="keyword">this</span>.printStream.println(msg);</span><br><span class="line">                &#125;<span class="keyword">catch</span> (Exception e)&#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;write 异常退出：&quot;</span>+e.getMessage());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">stopWriter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            done = <span class="keyword">true</span>;</span><br><span class="line">            CloseUtils.close(printStream);</span><br><span class="line">            executorService.shutdownNow();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>完整代码放在 <a class="link"   target="_blank" rel="noopener" href="https://github.com/imlgw/socketDemo" >github<i class="fas fa-external-link-alt"></i></a> 有一点需要注意的是这里用了一个单线程池去处理服务端发送消息的功能，这里其实用线程通信机制<code>wait/notify</code>也可以做到但是相比使用线程池会复杂许多。</p>
<h3 id="局域网聊天室实现"><a href="#局域网聊天室实现" class="headerlink" title="局域网聊天室实现"></a>局域网聊天室实现</h3><blockquote>
<p>这里的聊天室，其实关键的地方就在于对客户端发送的消息交由服务端进行转发。</p>
</blockquote>
<p>基于上面的进行改造</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 回调接口</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ClientHandleCallBack</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 将已经关闭的handle暴露给TCPServer然后从list中移除</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">onSelfClosed</span><span class="params">(ClientHandler clientHandler)</span></span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 将消息交给服务器转发</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> clientHandler</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> msg</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">onNewMessageArrived</span><span class="params">(ClientHandler clientHandler,String msg)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>增加一个消息抵达的接口，然后为了避免阻塞交给异步的单线程池去处理</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onNewMessageArrived</span><span class="params">(ClientHandler clientHandler, String msg)</span> </span>&#123;</span><br><span class="line">       System.out.println(<span class="string">&quot;Receive from:&quot;</span>+clientHandler.getClientInfo()+<span class="string">&quot; msg:&quot;</span>+msg);</span><br><span class="line">       forwardThreadPool.submit(()-&gt;&#123;</span><br><span class="line">           <span class="keyword">for</span> (ClientHandler clientHandle : clientHandles) &#123;</span><br><span class="line">               <span class="comment">//跳过自己</span></span><br><span class="line">               <span class="keyword">if</span>(clientHandle.equals(clientHandler))&#123;</span><br><span class="line">                   <span class="keyword">continue</span>;</span><br><span class="line">               &#125;</span><br><span class="line">               <span class="comment">//对其他客户端发送消息</span></span><br><span class="line">               clientHandle.send(msg);</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

<p>详细代码 见<a class="link"   target="_blank" rel="noopener" href="https://github.com/imlgw/socketDemo" >Github<i class="fas fa-external-link-alt"></i></a></p>

        </div>

        
            <div class="post-copyright-info">
                <div class="article-copyright-info-container">
    <ul>
        <li>本文标题：Socket网络编程</li>
        <li>本文作者：Resolmi</li>
        <li>创建时间：2019-07-19 00:00:00</li>
        <li>
            本文链接：https://imlgw.top/2019/07/19/df38ad26/
        </li>
        <li>
            版权声明：本博客所有文章除特别声明外，均采用 <a class="license" target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh">BY-NC-SA</a> 许可协议。转载请注明出处！
        </li>
    </ul>
</div>

            </div>
        

        
            <div class="article-nav">
                
                    <div class="article-prev">
                        <a class="prev"
                           rel="prev"
                           href="/2019/07/20/41e491de/"
                        >
                            <span class="left arrow-icon flex-center">
                              <i class="fas fa-chevron-left"></i>
                            </span>
                            <span class="title flex-center">
                                <span class="post-nav-title-item">LeetCode滑动窗口</span>
                                <span class="post-nav-item">上一篇</span>
                            </span>
                        </a>
                    </div>
                
                
                    <div class="article-next">
                        <a class="next"
                           rel="next"
                           href="/2019/06/06/f990e699/"
                        >
                            <span class="title flex-center">
                                <span class="post-nav-title-item">JMeter压测</span>
                                <span class="post-nav-item">下一篇</span>
                            </span>
                            <span class="right arrow-icon flex-center">
                              <i class="fas fa-chevron-right"></i>
                            </span>
                        </a>
                    </div>
                
            </div>
        

        
            <div class="comment-container">
                <div class="comments-container">
    <div id="comment-anchor"></div>
    <div class="comment-area-title">
        <i class="fas fa-comments">&nbsp;评论</i>
    </div>
    

        
            <section class="disqus-comments">
<div id="disqus_thread">
  <noscript>Please enable JavaScript to view the <a target="_blank" rel="noopener" href="//disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</div>
</section>

<script>
var disqus_shortname = 'imlgw';

var disqus_url = 'https://imlgw.top/2019/07/19/df38ad26/';

(function(){
  var dsq = document.createElement('script');
  dsq.type = 'text/javascript';
  dsq.async = true;
  dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
  (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>

<script id="dsq-count-scr" src="//imlgw.disqus.com/count.js" async></script>
        
    
</div>

            </div>
        
    </div>
</div>


                
            </div>

        </div>

        <div class="page-main-content-bottom">
            <footer class="footer">
    <div class="info-container">
        <div class="copyright-info info-item">
            &copy;
            
              <span>2018</span>&nbsp;-&nbsp;
            
            2021&nbsp;<i class="fas fa-heart icon-animate"></i>&nbsp;<a href="/">Resolmi</a>
        </div>
        
            <script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
            <div class="website-count info-item">
                
                    <span id="busuanzi_container_site_uv">
                        访问人数&nbsp;<span id="busuanzi_value_site_uv"></span>&ensp;
                    </span>
                
                
                    <span id="busuanzi_container_site_pv">
                        总访问量&nbsp;<span id="busuanzi_value_site_pv"></span>
                    </span>
                
            </div>
        
        
            <div class="icp-info info-item"><a target="_blank" rel="nofollow" href="https://beian.miit.gov.cn">鄂ICP备18011208号</a></div>
        
    </div>
</footer>

        </div>
    </div>

    
        <div class="post-tools">
            <div class="post-tools-container">
    <ul class="tools-list">
        <!-- TOC aside toggle -->
        
            <li class="tools-item page-aside-toggle">
                <i class="fas fa-outdent"></i>
            </li>
        

        <!-- go comment -->
        
            <li class="go-comment">
                <i class="fas fa-comment"></i>
            </li>
        
    </ul>
</div>

        </div>
    

    <div class="right-bottom-side-tools">
        <div class="side-tools-container">
    <ul class="side-tools-list">
        <li class="tools-item tool-font-adjust-plus flex-center">
            <i class="fas fa-search-plus"></i>
        </li>

        <li class="tools-item tool-font-adjust-minus flex-center">
            <i class="fas fa-search-minus"></i>
        </li>

        <li class="tools-item tool-expand-width flex-center">
            <i class="fas fa-arrows-alt-h"></i>
        </li>

        <li class="tools-item tool-dark-light-toggle flex-center">
            <i class="fas fa-moon"></i>
        </li>

        <!-- rss -->
        

        

        <li class="tools-item tool-scroll-to-bottom flex-center">
            <i class="fas fa-arrow-down"></i>
        </li>
    </ul>

    <ul class="exposed-tools-list">
        <li class="tools-item tool-toggle-show flex-center">
            <i class="fas fa-cog fa-spin"></i>
        </li>
        
            <li class="tools-item tool-scroll-to-top flex-center">
                <i class="arrow-up fas fa-arrow-up"></i>
                <span class="percent"></span>
            </li>
        
    </ul>
</div>

    </div>

    
        <aside class="page-aside">
            <div class="post-toc-wrap">
    <div class="post-toc">
        <ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#Socket%E6%A6%82%E8%BF%B0"><span class="nav-number">1.</span> <span class="nav-text">Socket概述</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#TCP"><span class="nav-number">2.</span> <span class="nav-text">TCP</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%AE%80%E5%8D%95%E7%9A%84Socket%E5%B0%8F%E6%A1%88%E4%BE%8B"><span class="nav-number">2.1.</span> <span class="nav-text">简单的Socket小案例</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E4%BC%A0%E8%BE%93%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B"><span class="nav-number">2.2.</span> <span class="nav-text">传输基本数据类型</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#UDP"><span class="nav-number">3.</span> <span class="nav-text">UDP</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%8D%95%E6%92%AD"><span class="nav-number">3.1.</span> <span class="nav-text">单播</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%A4%9A%E6%92%AD-amp-%E5%B9%BF%E6%92%AD"><span class="nav-number">3.2.</span> <span class="nav-text">多播&amp;广播</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#UDP%E8%BE%85%E5%8A%A9TCP-%E5%AE%9E%E7%8E%B0%E7%82%B9%E5%AF%B9%E7%82%B9%E4%BC%A0%E8%BE%93"><span class="nav-number">4.</span> <span class="nav-text">UDP辅助TCP 实现点对点传输</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%B1%80%E5%9F%9F%E7%BD%91%E8%81%8A%E5%A4%A9%E5%AE%A4%E5%AE%9E%E7%8E%B0"><span class="nav-number">5.</span> <span class="nav-text">局域网聊天室实现</span></a></li></ol>
    </div>
</div>
        </aside>
    

    <div class="image-viewer-container">
    <img src="">
</div>


    
        <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
          <span class="search-input-field-pre">
            <i class="fas fa-keyboard"></i>
          </span>
            <div class="search-input-container">
                <input autocomplete="off"
                       autocorrect="off"
                       autocapitalize="off"
                       placeholder="搜索..."
                       spellcheck="false"
                       type="search"
                       class="search-input"
                >
            </div>
            <span class="popup-btn-close">
                <i class="fas fa-times"></i>
            </span>
        </div>
        <div id="search-result">
            <div id="no-result">
                <i class="fas fa-spinner fa-pulse fa-5x fa-fw"></i>
            </div>
        </div>
    </div>
</div>

    

</main>



<script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/utils.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/main.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/header-shrink.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/back2top.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/dark-light-toggle.js"></script>


    <script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/local-search.js"></script>



    <script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/code-copy.js"></script>



    <script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/lazyload.js"></script>


<div class="post-scripts pjax">
    
        <script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/left-side-toggle.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/libs/anime.min.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/toc.js"></script>
    
</div>


    <script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/libs/pjax.min.js"></script>
<script>
    window.addEventListener('DOMContentLoaded', () => {
        window.pjax = new Pjax({
            selectors: [
                'head title',
                '.page-container',
                '.pjax'
            ],
            history: true,
            debug: false,
            cacheBust: false,
            timeout: 0,
            analytics: false,
            currentUrlFullReload: false,
            scrollRestoration: false,
            // scrollTo: true,
        });

        document.addEventListener('pjax:send', () => {
            KEEP.utils.pjaxProgressBarStart();
        });

        document.addEventListener('pjax:complete', () => {
            KEEP.utils.pjaxProgressBarEnd();
            window.pjax.executeScripts(document.querySelectorAll('script[data-pjax], .pjax script'));
            KEEP.refresh();
        });
    });
</script>



<script src="https://cdn.jsdelivr.net/npm/live2d-widget@3.x/lib/L2Dwidget.min.js"></script><script>L2Dwidget.init({"pluginRootPath":"live2dw/","pluginJsPath":"lib/","pluginModelPath":"assets/","tagMode":false,"debug":false,"model":{"jsonPath":"https://cdn.jsdelivr.net/npm/live2d-widget-model-hijiki@1.0.5/assets/hijiki.model.json"},"display":{"superSample":2,"width":160,"height":320,"position":"right","hOffset":0,"vOffset":-70},"mobile":{"show":false,"scale":0.2},"log":false});</script></body>
</html>
